/*
 * FileTransferTcpClientSession.cpp
 *
 *  Created on: Sep 13, 2012
 *      Author: shawnmao
 */

#include "FileTransferTcpClientSession.h"
#include "FileBuffer.h"
#include "FileDriver.h"

FileTransferTcpClientSession::FileTransferTcpClientSession(string local_addr, uint16_t local_port,
														   string remote_addr, uint16_t remote_port,
														   string file_path, string file_name)
: TcpClientSession(local_addr, local_port, remote_addr, remote_port),
  m_transfer_state(TCP_FILE_TRANSFER_IDLE),
  m_file_path(file_path),
  m_file_name(file_name)
{
}

void FileTransferTcpClientSession::execute_cli_protocol()
{
	int ret_val;
	TcpMessageType msg_type;
	uint16_t msg_len;
	uint16_t recv_len;
	string recv_str;
	string send_str;

	/*receive "Session is OK" from server*/
	ret_val = recv_msg_type();
	if((-1 == ret_val) || (-2 == ret_val)) {
		return;
	}
	else if(TCP_CONTROL_MSG != ret_val) {
		close(m_conn_sock);
		return;
	}
	ret_val = recv_msg_len();
	if((-1 == ret_val) || (-2 == ret_val)) {
		return;
	}
	ret_val = recv_ctrl_msg(&recv_str, ret_val);
	if((-1 == ret_val) || (-2 == ret_val)) {
		return;
	}

#ifdef DEBUG_FILE_TRANSFER_TCP_CLIENT_SESSION
	cout<<"&Thread "<<pthread_self()<<" : The msg from server is: "<<recv_str<<endl;
#endif


	if(0 == recv_str.compare("Session is connected.")) {  // connection is established
#ifdef DEBUG_FILE_TRANSFER_TCP_CLIENT_SESSION
		cout<<"Establish a connection with server host"<<endl;
#endif

		/*send "OK" to server*/
		send_str.clear();
		send_str.append("OK");
		ret_val = send_ctrl_msg(send_str);
		if(-1 == ret_val) {
			return;
		}
	}
	else { // connection is not established
		cout<<"Tcp client session has not receive the correct "
				"response from server session, the response from server "
				"session is: "<<recv_str<<endl;
		close(m_conn_sock);
		return;
	}

	/*send file name to server*/
	send_str.clear();
	send_str.append(m_file_name);
	ret_val = send_ctrl_msg(send_str);
	if(-1 == ret_val) {
		return;
	}

	/*send file content to server*/
	ret_val = send_file();

	/*close socket normally*/
	close(m_conn_sock);
	m_transfer_state = TCP_FILE_TRANSFER_DONE;

#ifdef DEBUG_FILE_TRANSFER_TCP_CLIENT_SESSION
	cout<<"socket has been closed by client host."<<endl;
#endif
}

int FileTransferTcpClientSession::send_ctrl_msg(string send_msg)
{
	int ret_val;
	TcpMessageType msg_type = TCP_CONTROL_MSG;

	/*send control message type*/
	ret_val = send(m_conn_sock, (void*)&msg_type, sizeof(TcpMessageType), 0);
	if(-1 == ret_val) {
		cout<<"Fail to send control message type to server host"<<endl;
		close(m_conn_sock);
		return -1;
	}
	else if(ret_val < sizeof(TcpMessageType)) {
		cout<<"Wrong control message type has been sent to server host."<<endl;
		close(m_conn_sock);
		return -1;
	}

	/*send control message length*/
	uint16_t msg_len = send_msg.length();
	ret_val = send(m_conn_sock, (void*)&msg_len, 2, 0);
	if(-1 == ret_val) {
		cout<<"Fail to send control message length to server host"<<endl;
		close(m_conn_sock);
		return -1;
	}
	else if(ret_val < 2) {
		cout<<"Wrong control message length has been sent to server host."<<endl;
		close(m_conn_sock);
		return -1;
	}

	/*send control message content*/
	ret_val = send(m_conn_sock, (void*)send_msg.c_str(), send_msg.length(), 0);
	if(-1 == ret_val) {
		cout<<"Fail to send control message content to server host"<<endl;
		close(m_conn_sock);
		return -1;
	}
	else if(ret_val < send_msg.length()) {
		cout<<"Incomplete control message content has been sent to server host."<<endl;
		close(m_conn_sock);
		return -1;
	}

	return 0;
}

int FileTransferTcpClientSession::recv_msg_type()
{
	int ret_val;
	TcpMessageType msg_type;

	ret_val = recv(m_conn_sock, (void*)&msg_type, sizeof(TcpMessageType), 0);
	if(-1 == ret_val) {
		cout<<"Fail to receive message type form server host."<<endl;
		close(m_conn_sock);
		return -1;
	}
	else if(0 == ret_val) {
		cout<<"Server has shutdown the connection."<<endl;
		close(m_conn_sock);
		return -2;
	}
	else if(ret_val < sizeof(TcpMessageType)) {
		cout<<"Wrong message type has been received from server host"<<endl;
		close(m_conn_sock);
		return -1;
	}

	return msg_type;
}

int FileTransferTcpClientSession::recv_msg_len()
{
	int ret_val;
	uint16_t msg_len;

	ret_val = recv(m_conn_sock, (void*)&msg_len, 2, 0);
	if(-1 == ret_val) {
		cout<<"Fail to receive message length from server host."<<endl;
		close(m_conn_sock);
		return -1;
	}
	else if(0 == ret_val) {
		cout<<"Server has shutdown the connection."<<endl;
		close(m_conn_sock);
		return -2;
	}
	else if(2 > ret_val) {
		cout<<"Wrong message length has been received from server host."<<endl;
		close(m_conn_sock);
		return -1;
	}

	return msg_len;
}

int FileTransferTcpClientSession::recv_ctrl_msg(string* recv_msg, ssize_t msg_len)
{
	int ret_val;
	ssize_t recv_size = 0;
	char buf_for_recv[msg_len + 1];

	do {
		bzero(buf_for_recv, sizeof(buf_for_recv));
		ssize_t ret_val = recv(m_conn_sock, (void*)buf_for_recv, (msg_len - recv_size), 0);
		if(-1 == ret_val) {
			cout<<"Fail to receive control message from server, "
				  "the failure reason is: "<<errno<<endl;
			close(m_conn_sock);
			return -1;
		}
		else if(0 == ret_val) {
			cout<<"Server has shutdown the connection."<<endl;
			close(m_conn_sock);
			return -2;
		}

		(*recv_msg).append(buf_for_recv);
		recv_size += ret_val;
	}while(recv_size < msg_len);

	return recv_size;
}

int FileTransferTcpClientSession::send_file()
{
	int ret_val;
	TcpMessageType msg_type = TCP_FILE_TRANSFER;
	int buffer_size = 0;
	FileDriver file_driver;
	FileBuffer file_buffer;
	uint64_t file_len, send_size;

	/*send file transfer message type*/
	ret_val = send(m_conn_sock, (void*)&msg_type, sizeof(TcpMessageType), 0);
	if(-1 == ret_val) {
		cout<<"Fail to send file transfer message type to server host"<<endl;
		close(m_conn_sock);
		return -1;
	}
	else if(ret_val < sizeof(TcpMessageType)) {
		cout<<"Wrong file transfer message type has been sent to server host."<<endl;
		close(m_conn_sock);
		return -1;
	}

	/*send file transfer message length*/
	file_driver.file_open(m_file_path + m_file_name);
	file_len = file_driver.file_size();
	ret_val = send(m_conn_sock, (void*)&file_len, sizeof(file_len), 0);
	if(-1 == ret_val) {
		cout<<"Fail to send file transfer message length to server host"<<endl;
		close(m_conn_sock);
		return -1;
	}
	else if(ret_val < 2) {
		cout<<"Wrong file transfer message length has been sent to server host."<<endl;
		close(m_conn_sock);
		return -1;
	}

	cout<<"The file length is: "<<file_len<<endl;

	/*send file content*/
	send_size = 0;
	do {
		buffer_size = (FILE_TRANSFER_BUFFER_SZIE < (file_len - send_size)) ? FILE_TRANSFER_BUFFER_SZIE : (file_len - send_size);
		file_buffer.buffer_free();
		file_buffer.buffer_new(buffer_size);
		int read_size = file_driver.file_read(file_buffer.buffer_get(), buffer_size);
		if(-1 == read_size) {
			close(m_conn_sock);
			return -1;
		}

//		cout<<"Buffer sent to server is: "<<endl<<(char*)(file_buffer.buffer_get())<<endl;
		cout<<"The length of Buffer sent is: "<<read_size<<endl;

		ret_val = send(m_conn_sock, file_buffer.buffer_get(), read_size, 0);
		if(-1 == ret_val) {
			cout<<"Fail to send file content to server host."<<endl;
			close(m_conn_sock);
			return -1;
		}

		send_size += ret_val;
		cout<<"The size of sent buffer is: "<<send_size<<endl;
	}while(send_size < file_len);

	file_buffer.buffer_free();
	file_driver.file_close();
	return 0;
}
